#include <fcntl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

#define SOCKET_ADDR "127.0.0.1"
#define BUFFER_SIZE 1024

typedef int (fill_data)(void* data);
typedef struct
{
    void* data;
    int len;
}socket_buffer_t;

typedef struct
{
    /* ipv4 or ipv6 */
    int domain;
    /* tcp or udp */
    int type;
    /* server file description*/
    int s_fd;
    /* client file description*/
    int c_fd;
    /* socket address */
    struct sockaddr_in addr;
    /* store data needed to be transfered*/
    socket_buffer_t* buffer;

    int buffer_num;
} socket_handle_t;

socket_handle_t socket_handle;

int
create_socket()
{
    socket_handle_t* sh=&socket_handle;
    int err;
    
    /* init socket args*/
    sh->domain=AF_INET;
    sh->type=SOCK_STREAM;

    err=socket(sh->domain,sh->type,0);
    if(err<0){
        /* failed to create socket */
        return 1;
    }
    else{
        sh->s_fd=err;
        return 0;
    }
    // int flags=fcntl(sh->fd,F_GETFL,0);
    // /* set no_block mode for socket */
    // fcntl(sh->fd,F_SETFL,flags|O_NONBLOCK);
}

int
bind_ip()
{
    socket_handle_t* sh=&socket_handle;
    /*init socket*/
    sh->addr.sin_family=AF_INET;
    sh->addr.sin_port=8000;
    sh->addr.sin_addr.s_addr=inet_addr(SOCKET_ADDR);

    /* bind local ip*/
    int err;
    err=bind(sh->s_fd,(struct sockaddr*)&sh->addr,sizeof(sh->addr));
    if(err<0){
        // printf("%d\n",err);
        return 1;
    }
    else{
        return 0;
    }
}

int
transmit(fill_data fill,void* data)
{
    socket_handle_t* sh=&socket_handle;
    /*fill data*/
    fill(data);
    
    // printf("%s\n",(char*)sh->buffer.data);
    /*transfer data*/
    int buffer_num=sizeof(sh->buffer)/sizeof(socket_buffer_t);
    int next=0;
    for(int i=0;i<buffer_num;i++)
    {
        int count_out;
        do {
            count_out=0;
            count_out=write(sh->c_fd,sh->buffer[i].data+next,sh->buffer[i].len);
            if(count_out<0)
            {
                printf("failed to send data!\n");
                return 1;
            }
            else if(count_out<sh->buffer[i].len)
            {
                next=+count_out;
            }
            else
            {
                /* free buffer*/
                free(sh->buffer->data);
                next=0;
                sh->buffer[i].len=0;
            }
        }while(next<sh->buffer->len);
    }
    return 0;
}

int
receive()
{
    socket_handle_t* sh=&socket_handle;
    int buffer_num=sh->buffer_num;

    printf("before!\nbuffer_num:%d\n",buffer_num);

    for(int i=0;i<buffer_num;i++)
    {
        sh->buffer[i].data=(void*)malloc(BUFFER_SIZE);
        int count_out=0;
        int next=0;
        do
        {
            count_out=read(sh->c_fd,sh->buffer[i].data+next,BUFFER_SIZE-next);
            if(count_out<0)
            {
                printf("failed to receive data!\n");
            }
            else if(count_out<BUFFER_SIZE-next){
                if(i==buffer_num-1)
                {
                    printf("heeee!\n");
                    break;
                }
                else
                {
                    next=+count_out;    
                }
                
            }
            else
            {
                break;
            }
        }while(next<BUFFER_SIZE);
    }

    printf("after\n");

    for(int i=0;i<buffer_num;i++)
    {
        printf("%s\n",(char*)sh->buffer[i].data);
    }

    return 0;
}

int
receive_once()
{
    socket_handle_t* sh=&socket_handle;
    
    /*allocate space for data*/
    sh->buffer=(socket_buffer_t*)malloc(sizeof(socket_buffer_t));
    sh->buffer->data=(void*)malloc(sh->buffer->len);

    int sum=0;
    do
    {
        int count_out=0;
        count_out=read(sh->c_fd,sh->buffer->data+sum,sh->buffer->len);
        if(count_out<=0){
            printf("failed to receive data!\n");
            return 1;
        }
        sum+=count_out;
    }while(sum<sh->buffer->len);

    // printf("data:%s\n",(char*)sh->buffer.data);
    return 0;
}

int
say_hello_to_client(void* data)
{
    socket_handle_t* sh=&socket_handle;
    char* str="hi, client!\n";
    sh->buffer->len=strlen(str);
    /* allocate space for buffer*/
    sh->buffer->data=(void*)malloc(sizeof(str));
    /* copy data to buffer*/
    strncpy((char*)sh->buffer->data,str,sh->buffer->len);
    return 0;
}

void
server_exit(int sig)
{
    socket_handle_t* sh=&socket_handle;
    close(sh->s_fd);
    close(sh->c_fd);
    exit(1);
}

void
send_kill(int sig)
{
    socket_handle_t* sh=&socket_handle;
    close(sh->s_fd);
    close(sh->c_fd);
    exit(0);
}

int main(int argc,char** argv){

    signal(SIGINT,server_exit);
    signal(SIGKILL,send_kill);

    int err;
    socklen_t addr_len;
    socket_handle_t* sh=&socket_handle;
    /* maximum of block queue */
    int count=1;

    /* store data length*/
    int length=atoi(argv[1]);
    // sh->buffer->len=length;

    /*receive repeat*/
    int buffer_num=(length+BUFFER_SIZE-1)/BUFFER_SIZE;
    sh->buffer=(socket_buffer_t*)malloc(sizeof(socket_buffer_t)*buffer_num);
    sh->buffer_num=buffer_num;

    err=create_socket();
    if(err!=0)
    {
        printf("failed to create socket!\n");
        return 1;
    }

    err=bind_ip();
    if(err!=0)
    {
        printf("failed to bind socket!\n");
        return 1;
    }

    /* listening socket */
    err=listen(sh->s_fd,count);

    /* accept connection*/
    while(1)
    {
        addr_len=sizeof(struct sockaddr_in);
        err=accept(sh->s_fd,(struct sockaddr*)&sh->addr,&addr_len);
        // printf("%d:%d\n",sh->fd,err);
        sh->c_fd=err;
        if(err==-1){
            printf("accept connection error!\n");
            return 1;
        }
        receive();
        close(sh->c_fd);
        close(sh->s_fd);
        printf("over!\n");
        return 0;
    }
    return 0;
}